/*
 * This program is used to config eth MAC 
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include <time.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <errno.h>

//#define DEBUG 
#ifdef DEBUG
	#define debug_print printf
#else
	#define debug_print
#endif

#define MAC_ALEN 6
#define MAC_CONFIG_FILE "/etc/macfile"
#define IFCOMMAND "ifconfig -a |grep eth |awk '{print $1}';"

typedef struct _mem_mac {
	unsigned char mac[MAC_ALEN];
	struct _mem_mac *next;
}mem_mac;

void produce_random_mac(unsigned char *mac);
void get_mac(int fd, unsigned char *mac);
void save_mac(int fd, mem_mac **mac_header);
void add_mac(mem_mac **mac_header, unsigned char *mac);
void free_mac(mem_mac *mac_header);
void config_mac(const char *name, unsigned char* mac);

bool check_mac_is_valid(unsigned char *mac);
void produce_random_mac(unsigned char *mac);

int main(int argc, char const *argv[])
{
	unsigned char mac[MAC_ALEN] = {0};  // mac length
	char name[12] = {0}; //max eth name length
	mem_mac *mac_header = NULL;
	int fd_file = 0;
	FILE *fp_cmd = NULL;
	char tmp = 'a';
	int i = 0;

	srand(time(0));

	fp_cmd = popen(IFCOMMAND, "r");
	if(fp_cmd == NULL) {
		printf("Can not find network interface name!!\n");
		return -1;
	}

	fd_file = open(MAC_CONFIG_FILE, O_RDWR | O_CREAT, 0664);
	if(fd_file < 0) {
		printf("open %s failed, configure mac from random number\n", MAC_CONFIG_FILE);
	}

	while(1)
	{
		tmp = fgetc(fp_cmd);
		if(tmp == EOF)
		{
			break;
		}
		else if(tmp == '\n')
		{
			get_mac(fd_file, mac);
			config_mac(name, mac);
			add_mac(&mac_header, mac);

			memset(name, 0, sizeof(name));/* for next use */
			memset(mac, 0, MAC_ALEN);
			i = 0; 
		}
		else
		{
			name[i++] = tmp;
			if(i > sizeof(name) -1)
			{
				printf("name = %s\n", name);
				printf("The interface name is too long for this program~\n");
				break;
			}
		}
	}
	pclose(fp_cmd);

	lseek(fd_file, 0, SEEK_SET);	
	save_mac(fd_file, &mac_header);
	close(fd_file);

	free_mac(mac_header);

	return 0;
}

void add_mac(mem_mac **mac_header, unsigned char *mac)
{
	mem_mac *tmp = *mac_header;
	mem_mac *node = NULL;
	
	node = malloc(sizeof(mem_mac));
	if(node == NULL) {
		return;
	}
	memset(node, 0, sizeof(mem_mac));
	memcpy(node->mac, mac, MAC_ALEN);

	if(tmp == NULL)
	{ 
		debug_print("%s -> %p\n", __FUNCTION__, node);
		*mac_header = node;
	}
	else
	{
		while(tmp->next != NULL)
		{
			tmp = tmp->next;
		}
		debug_print("%s -> %p\n", __FUNCTION__, node);
		tmp->next = node;
	}
}

void free_mac(mem_mac *mac_header)
{
	mem_mac *tmp = mac_header;

	if(tmp == NULL)
		return;

	while(tmp->next != NULL)
	{
		mac_header = mac_header->next;
		debug_print("%s -> %p\n", __FUNCTION__, tmp);
		free(tmp);
		tmp = mac_header;
	}
	debug_print("%s -> %p\n", __FUNCTION__, tmp);
	free(tmp);
}

bool check_mac_is_valid(unsigned char *mac)
{
	/* FF:FF:FF:FF:FF:FF is a multicast address so we don't need to
	 * explicitly check for it here. */
	 return !(0x01 & mac[0]) && (mac[0] | mac[1] | mac[2] | mac[3] | mac[4] | mac[5]);
}

void produce_random_mac(unsigned char *mac)
{
	int i;
	for(i = 0; i < MAC_ALEN; i++)
	{
		mac[i] = rand() % 0xff;
	}
	mac[0] &= 0xfe;	/* clear multicast bit */
	mac[0] |= 0x02;	/* set local assignment bit (IEEE802) */
}

void get_mac(int fd, unsigned char *mac)
{
	int res;

	res = read(fd, mac, MAC_ALEN);
	if(res != MAC_ALEN)
	{
		produce_random_mac(mac);
	}
	debug_print("%s -> %02x:%02x:%02x:%02x:%02x:%02x\n", 
			__FUNCTION__,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
	while(!check_mac_is_valid(mac))
	{
		produce_random_mac(mac);
	}
}

void save_mac(int fd, mem_mac **mac_header)
{	
	mem_mac *tmp = *mac_header;
	mem_mac *node = NULL;

	if(tmp == NULL)
		return;

	while(tmp->next != NULL)
	{
		node = tmp;
		write(fd, node->mac, MAC_ALEN);
		debug_print("%s -> %02x:%02x:%02x:%02x:%02x:%02x\n", 
				__FUNCTION__,node->mac[0],node->mac[1],node->mac[2],node->mac[3],node->mac[4],node->mac[5]);
		tmp = tmp->next;
	}
	write(fd, tmp->mac, MAC_ALEN);
	debug_print("%s -> %02x:%02x:%02x:%02x:%02x:%02x\n", 
			__FUNCTION__,tmp->mac[0],tmp->mac[1],tmp->mac[2],tmp->mac[3],tmp->mac[4],tmp->mac[5]);
}

void config_mac(const char *name, unsigned char* mac)
{/* name should not contain '\n' */
	unsigned char mac_sh[120] = {0};
	sprintf(mac_sh,"ifconfig %s down;ifconfig %s hw ether %02x:%02x:%02x:%02x:%02x:%02x;",
			name, name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
	debug_print("%s -> %s\n", __FUNCTION__, mac_sh);
	system(mac_sh);
}


